/**
 * DSM - Distributed Search Manager
 * Developed by Milspec Research International Pty Ltd
 * $Author: bslattery $
 * $Revision: 1.8 $
 * $Date: 2007/09/13 04:08:45 $
 * (c)Copyright 2004
 * education.au limited
 * DEST
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the names education.au limited, DEST nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 * To the extent permitted by law, the copyright owners of this software and
 * its contributors:
 * (i) exclude all warranties in relation to the software; and
 * (ii) exclude liability for all losses, costs, expenses and damages arising
 * in any way from the use of the software whether arising from or in
 * relation to breach of contract, negligence or any other tort, in equity
 * or otherwise. If the software is in breach of a warranty which is
 * implied by law, the copyright owners and contributors liability is
 * limited to the replacement of the software.
 *
 * @author	gsingh
 */

package au.edu.educationau.opensource.dsm.worker;

import au.edu.educationau.opensource.dsm.DeusExMachina;
import au.edu.educationau.opensource.dsm.obj.SMEvents;
import au.edu.educationau.opensource.dsm.obj.SearchCriteria;
import au.edu.educationau.opensource.dsm.util.Flog;

/**
 * A java.lang.Runnable implemented class that is capable of multi-threading
 * au.edu.educationau.opensource.dsm.worker.SearchTask objects.
 */

public class SearchJob extends java.lang.Object implements java.lang.Runnable {
	private SearchTask[] tasks = null;
	private long token = 0;
	private SearchCriteria searchCriteria = null;
	private long timeCreated = 0;

	/**
	 * Primary constructor
	 * 
	 * @param searchCriteria
	 */
	public SearchJob(SearchCriteria searchCriteria) {
		this.searchCriteria = searchCriteria;
		this.token = searchCriteria.getToken();
		this.timeCreated = System.currentTimeMillis();
	}

	/** Returns the token of this job */
	public long getToken() {
		return token;
	}

	/** Returns the search criteria associated with this job */
	public SearchCriteria getSearchCriteria() {
		return searchCriteria;
	}

	/**
	 * Adds the tasks to its to-do list.
	 * 
	 * @param tasks
	 */
	public synchronized void loadTasks(SearchTask[] tasks) {
		if (null != tasks && null == this.tasks) {
			this.tasks = tasks;
		}
	}

	/**
	 * Dumps Information log entries of the states of the tasks
	 * 
	 * @deprecated
	 * @see au.edu.educationau.opensource.dsm.worker.SearchTask instead. The
	 *      summary is done at the task level now.
	 */
	public void summary() {
		for (int i = 0; i < tasks.length; i++) {
			Flog.info("" + token + " " + tasks[i].toString(), "" + tasks[i].getTotalFound() + " results so far. S[" + tasks[i].getStatus() + "][" + tasks[i].getTimeTaken() + "ms]");
		}
	}

	/**
	 * Returns the total number of results returned across all tasks (this value
	 * may increase if some tasks haven't finished executing yet)
	 */
	public int getTotalResultsSoFar() {
		int totalResults = 0;

		for (int i = 0; i < tasks.length; i++) {
			totalResults += tasks[i].getResultCount();
		}
		return totalResults;
	}

	/**
	 * Returns the time (in milliseconds) taken by the slowest task, or if not
	 * yet finished, the elapsed time since the search was started
	 */
	public long getTimeSpentSearchingSoFar() {
		if (isFinished()) {
			long maxTime = 0;
			for (int i = 0; i < tasks.length; i++) {
				maxTime = Math.max(maxTime, tasks[i].getTimeTaken());
			}
			return maxTime;
		} else {
			return System.currentTimeMillis() - timeCreated;
		}
	}

	/** Checks if one task is finished processing */
	public boolean isOneTaskFinished() {
		boolean finished = false;

		for (int i = 0; i < tasks.length; i++) {
			if (tasks[i].getStatus() == SMEvents.SEARCH_ADAPTER_STATUS_SUCCESSFUL) {
				finished = true;
				break;
			}
		}
		return finished;
	}

	/** Checks if one task has finished processing AND has results returned */
	public boolean isOneTaskFinishedWithResults() {
		int totalFinishedTasksResultCount = 0;

		for (int i = 0; i < tasks.length; i++) {
			totalFinishedTasksResultCount += tasks[i].getResultCount();
		}
		return (totalFinishedTasksResultCount > 0 || isFinished());
	}

	/** Forces all tasks to finish what they are doing immediately! */
	public void terminate() {
		for (int i = 0; i < tasks.length; i++) {
			tasks[i].terminate();
		}
	} // terminateJob

	/**
	 * Checks if the job is finished by checking if all the tasks have either
	 * been successful or have failed
	 */
	public boolean isFinished() {
		boolean finished = false;

		if (null == tasks) {
			return true;
		}

		for (int i = 0; i < tasks.length; i++) {
			if (tasks[i].getStatus() == SMEvents.SEARCH_ADAPTER_STATUS_SUCCESSFUL || tasks[i].getStatus() == SMEvents.SEARCH_ADAPTER_STATUS_FAILED) {
				finished = true;
			} else {
				finished = false;
				break;
			}
		}
		return finished;
	}

	/** Executes each task using the threadpool */
	public void run() {
		if (null != tasks && tasks.length > 0) {
			for (int i = 0; i < tasks.length; i++) {
				SearchTask task = tasks[i];
				try {
					DeusExMachina._ThreadPool().execute(task);
				} catch (java.lang.InterruptedException ie) { /* quiet */
				}
			}
		}
	}

	/** Returns the time this job was initiated */
	public long getTimeCreated() {
		return this.timeCreated;
	}

	/**
	 * Compares a search job by token.
	 * 
	 * @param obj
	 */
	public boolean equals(Object obj) {
		if (null == obj) {
			return false;
		} else {
			return ((SearchJob) obj).getToken() == this.token;
		}
	}
}
